# coding=utf-8
from flask import Blueprint, request, jsonify
from model import ShopType, Shop
from setting import Prefixs, is_exist
from utils.redis import Redis
from utils import restful
from utils.Lock import ExcludeLock
from form.shop import UpdateShopForm
from exts import db
from time import sleep
from sqlalchemy import func

bp = Blueprint('shop', __name__, url_prefix='/shop')


@bp.route('/of/type')
def queryShopByType():
    current = request.args.get("current", type=int, default=1)
    type_id = request.args.get("typeId")
    x = request.args.get("x")
    y = request.args.get("y")
    # 分页参数
    count = 6
    # redis key
    key = Prefixs.shop_geo + str(type_id)
    # 是否需要根据坐标查询,如果不需要
    if not x or not y:
        shop_list = [shop.to_dict() for shop in
                     Shop.query.filter_by(type_id=type_id).limit(count).offset((current - 1) * count).all()]
        if not shop_list:
            return restful.fail("没有更多信息了！")
        return jsonify({"success": True, "data": shop_list})
    # 如果需要根据坐标查询,查询30km以内的所有其他商铺
    all_shop_id_dist = Redis.geosearch(key, x=x, y=y, radius=30 * 1000, count=count * current, withdist=True)
    # 因为redis获取坐标不能分页，需要我们手动分页
    shops_id_dist = all_shop_id_dist[(current - 1) * count:count * current]
    if not shops_id_dist:
        return restful.fail("没有更多店铺了！")
    # 每一条数据都加上距离
    shops_id = [item[0] for item in shops_id_dist]
    shops_dist = [item[1] for item in shops_id_dist]
    shops_info = [shop.to_dict() for shop in db.session.query(Shop).filter(Shop.id.in_(shops_id)).order_by(
        func.field(Shop.id, *shops_id)).all()]
    for i in range(len(shops_info)):
        shops_info[i]['distance'] = shops_dist[i]
    return restful.success(data=shops_info)


def queryShopByIdHelper(id, num):
    if num == 3:
        return restful.fail(errorMsg="服务器繁忙,请稍后再试！"), 404
    key = Prefixs.shop_cache_prefix + str(id)
    # 1 从缓存中拿商店数据
    shop_info = Redis.hgetall(key)
    # 2 如果有缓存记录,直接返回结果 shop_info[]是因为需要防止缓存击穿配合使用
    if shop_info:
        if int(shop_info[is_exist]):  # 如果is_exist为1,代表有shop数据
            return restful.success(data=shop_info)
        else:  # is_exist为0,说明没有相关数据，是空数据，用来预防缓存穿透
            return restful.fail("店铺不存在!"), 404
    # 3 如果没有缓存记录,从数据库中拿,使用互斥锁，尝试获取锁资源
    lock_key = Prefixs.lock_shop_key + str(id)  # key = lock:shop:id
    # 4 如果没有拿到锁资源,递归调用直到拿到锁资源
    try:
        if not ExcludeLock.tryLock(lock_key):
            sleep(0.2)  # 休眠200ms
            return queryShopByIdHelper(id, num + 1)  # 递归继续执行
        # 5 如果拿到锁资源,尝试从数据库拿到数据，重建缓存
        shop_info = Shop.query.get(id)
        # 5-1 如果数据库也没有,返回404 修改为 -> 数据库没有则在缓存设置标识符，避免缓存穿透
        if not shop_info:
            Redis.hset(key, is_exist, 0)  # 缓存穿透了，设置缓存中is_exist标识为0
            Redis.expire(key, 60 * 2)
            return restful.fail(errorMsg="店铺不存在！"), 404
        # 5-2 如果数据库有，存入缓存
        shop_info = shop_info.to_dict()
        shop_info[is_exist] = 1
        if Redis.hmset(key, shop_info):
            Redis.expire(key, 60 * 5)
    except Exception as e:
        print(e)
    finally:
        # 6 最后释放互斥锁
        ExcludeLock.unLock(lock_key)
    print(shop_info)
    return restful.success(data=shop_info)


@bp.route("/<int:id>")
def queryShopById(id):
    return queryShopByIdHelper(id, 0)  # 递归次数初始为0


@bp.route('/updateShop', methods=["POST"])
def updateShop():
    form = UpdateShopForm(data=request.json)
    if not form.validate():
        return restful.fail(errorMsg=form.errors)
    id = form.id.data
    if not id:
        return restful.fail(errorMsg="id无效！")
    # 更新数据库
    shop = Shop.query.get(id)
    shop.area, shop.open_hours, shop.address, shop.type_id = form.area.data, form.open_hours.data, form.address.data, form.type_id.data
    shop.name, shop.images, shop.x, shop.y = form.name.data, form.images.data, form.x.data, form.y.data
    db.session.commit()
    # 删除缓存
    key = Prefixs.shop_cache_prefix + str(id)
    Redis.delete(key)
    return restful.success()


@bp.route('/of/name', methods=['GET'])
def query_shop_name():
    name = request.args.get('name', default="")
    if not name:
        # 没有输入名字默认返回全部商店信息
        shops = [shop.to_dict() for shop in Shop.query.all()]
    else:
        shops = [shop.to_dict() for shop in Shop.query.filter(Shop.name.contains(name)).all()]
    return restful.success(data=shops)
